/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-12
 * V4.0
 */
/*
 * $Header: /cvsbase/bengine/src/com/phenix/share/tools/Base64.java,v 1.1 2012/09/14 00:59:43 mbg Exp $
 * $Author: mbg $
 * $Revision: 1.1 $
 * $Date: 2012/09/14 00:59:43 $
 *
 * ====================================================================
 *
 * Copyright (C) 2002-2006 by MyVietnam.net
 *
 * All copyright notices regarding MyVietnam and MyVietnam CoreLib
 * MUST remain intact in the scripts and source code.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Correspondence and Marketing Questions can be sent to:
 * info at MyVietnam net
 *
 */
/*
 * $Header: /cvsbase/bengine/src/com/phenix/share/tools/Base64.java,v 1.1 2012/09/14 00:59:43 mbg Exp $
 * $Revision: 1.1 $
 * $Date: 2012/09/14 00:59:43 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *	notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *	notice, this list of conditions and the following disclaimer in
 *	the documentation and/or other materials provided with the
 *	distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *	any, must include the following acknowlegement:
 *	   "This product includes software developed by the
 *		Apache Software Foundation (http://www.apache.org/)."
 *	Alternately, this acknowlegement may appear in the software itself,
 *	if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
 *	Foundation" must not be used to endorse or promote products derived
 *	from this software without prior written permission. For written
 *	permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *	nor may "Apache" appear in their names without prior written
 *	permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * [Additional notices, if required by prior licensing conditions]
 *
 */
package com.jphenix.share.tools;

import com.jphenix.standard.docs.ClassInfo;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;


/**
 * Base64 加/解密算法
 * 
 * com.jphenix.share.tools.Base64
 * 
 * 由于Base64加密后的字符串中包含加号或斜杠（请看码表），导致前端
 * 经Base64加密后的信息提交到后台，加号跟斜杠提交到后台时，可能被转义，
 * 导致到后台接收到的数据是错的。
 * 
 * 2018-11-29 增加了RF2405标准编码输出（即：输出的Base64编码，每隔76个字符换行）
 *            解密时：过滤掉操蛋的RF2405标准规定（规定加密后的字符串，每个76字节做一次换行） 解码还用原来的方法，兼容RF2405标准
 * 
 * 2020-04-03 增加了方法urlE64，将字符串做Base64编码后，再做URL格式编码
 * 
 * 故增加了项目内使用的方法
 * 
 * nativeE64 编码
 * nativeD64 解码
 * 
 * @author 刘虻
 * 2008-8-6下午01:31:20
 */
@ClassInfo({"2020-04-03 10:03","Base64 加/解密算法"})
public final class  Base64 {
	
	protected final static byte WHITE_SPACE_ENC		  = -5; // Indicates white space
	protected final static byte EQUALS_SIGN_ENC		  = -1; // Indicates equals sign
	protected final static byte EQUALS_SIGN			  = (byte) '=';
	
	protected final static int  BASELENGTH			  = 255;
	protected final static int  LOOKUPLENGTH		  = 64;
	protected final static int  TWENTYFOURBITGROUP	  = 24;
	protected final static int  EIGHTBIT				  = 8;
	protected final static int  SIXTEENBIT			  = 16;
	//protected final static int  SIXBIT				  = 6;
	protected final static int  FOURBYTE				  = 4;
	protected final static int  SIGN					  = -128;
	protected final static byte PAD					  = (byte) '=';
	
	//标准码表
	protected static byte [] base64Alphabet			  = {
		-1, -1, -1, -1, -1, -1, -1, -1,
		-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
		-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
		-1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1,
		-1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
		13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1,
		-1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
		41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
	};
	protected static byte [] lookUpBase64Alphabet	      = {
			'A', 'B', 'C', 'D', 'E', 'F',								   /* 索引 0 ~ 5*/
			'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',  /* 索引6 ~ 18*/
			'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',  /* 索引 19 ~ 31*/
			'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',  /* 索引 32 ~ 44*/
			't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',  /* 索引 45 ~ 57*/
			'6', '7', '8', '9', '+', '/'									 /* 索引58 ~ 63*/	
	   };
	
	//内部项目使用的码表，替换掉加号和斜杠
	protected static byte [] nativeBase64Alphabet		  = {
			-1, -1, -1, -1, -1, -1, -1, -1,
			-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
			-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
			-1, -1/*原先是62*/, -1, -1, 62/*原先是-1*/, -1/*原先是63*/, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1,
			-1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
			13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1,
			63/*原先是-1*/, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
			41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
		};
	protected static byte [] nativeLookUpBase64Alphabet = {
			 'A', 'B', 'C', 'D', 'E', 'F', /* 索引 0 ~ 5*/
			 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',  /* 索引6 ~ 18*/
			 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',  /* 索引 19 ~ 31*/
			 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',  /* 索引 32 ~ 44*/
			 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',  /* 索引 45 ~ 57*/
			 '6', '7', '8', '9', '.'/*原先是字符+*/, '_'/*原先是字符/ */	   /* 索引58 ~ 63*/	
	};
   
	/**
	 * 是否为Base64编码
	 * @author 刘虻
	 * 2007-10-10下午05:03:57
	 * @param isValidString 测试编码
	 * @return true是
	 */
	public static boolean isBase64( String isValidString){
		return isArrayByteBase64(isValidString.getBytes());
	}

	/**
	 * 是否为Base64编码
	 * @author 刘虻
	 * 2007-10-10下午05:04:18
	 * @param octect 测试编码
	 * @return true是
	 */
	public static boolean isBase64( byte octect ){
		//shall we ignore white space? JEFF??
		return (octect == PAD || base64Alphabet[octect] != -1);
	}

	/**
	 * 是否为Base64编码
	 * @author 刘虻
	 * 2007-10-10下午05:05:31
	 * @param arrayOctect 测试编码
	 * @return true是
	 */
	public static boolean isArrayByteBase64( byte[] arrayOctect ){
		int length = arrayOctect.length;
		if (length == 0){
			// shouldn't a 0 length array be valid base64 data?
			// return false;
			return true;
		}
		for (int i=0; i < length; i++){
			if ( !Base64.isBase64(arrayOctect[i]) ) {
                return false;
            }
		}
		return true;
	}
	
	/**
	 * 解码
	 * @author 刘虻
	 * 2008-7-9上午08:10:08
	 * @param source 源
	 * @param off 起始字符位置
	 * @param len 长度
	 * @return 解码后的信息
	 */
	public static byte[] decode(byte[] source, int off, int len) {
		int len34 = len * 3 / 4;
		byte[] outBuff = new byte[len34]; // Upper limit on size of output
		int outBuffPosn = 0;

		byte[] b4 = new byte[4];
		int b4Posn = 0;
		int i = 0;
		byte sbiCrop = 0;
		byte sbiDecode = 0;
		for (i = off; i < off + len; i++) {
			//过滤掉操蛋的RF2405标准规定（规定加密后的字符串，每个76字节做一次换行）
			if(source[i]=='\r' || source[i]=='\n') {
				continue;
			}
			sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
			sbiDecode = base64Alphabet[sbiCrop];

			if (sbiDecode >= WHITE_SPACE_ENC) // Whitesp ace,Eq ualssi gnor be
			// tter
			{
				if (sbiDecode >= EQUALS_SIGN_ENC) {
					b4[b4Posn++] = sbiCrop;
					if (b4Posn > 3) {
						outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
						b4Posn = 0;

						// If that was the equals sign, break out of 'for' loop
						if (sbiCrop == EQUALS_SIGN) {
                            break;
                        }
					} // end if: quartet built

				} // end if: equals sign or better

			} // end if: white space, equals sign or better
			else {
				System.err.println("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
				return null;
			} // end else:
		} // each input character

		byte[] out = new byte[outBuffPosn];
		System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
		return out;
	} // end decode

	
	
	/**
	 * 内部使用的解码方法
	 * @author 刘虻
	 * 2008-7-9上午08:10:08
	 * @param source 待解码的内容
	 * @param off 起始位置
	 * @param len 长度
	 * @return 解码后的信息
	 */
	private static byte[] nativeDecode(byte[] source, int off, int len) {
		int len34 = len * 3 / 4;
		byte[] outBuff = new byte[len34]; // Upper limit on size of output
		int outBuffPosn = 0;

		byte[] b4 = new byte[4];
		int b4Posn = 0;
		int i = 0;
		byte sbiCrop = 0;
		byte sbiDecode = 0;
		for (i = off; i < off + len; i++) {
			sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
			sbiDecode = nativeBase64Alphabet[sbiCrop];

			if (sbiDecode >= WHITE_SPACE_ENC) // Whitesp ace,Eq ualssi gnor be
			// tter
			{
				if (sbiDecode >= EQUALS_SIGN_ENC) {
					b4[b4Posn++] = sbiCrop;
					if (b4Posn > 3) {
						outBuffPosn += nativeDecode4to3(b4, 0, outBuff, outBuffPosn);
						b4Posn = 0;

						// If that was the equals sign, break out of 'for' loop
						if (sbiCrop == EQUALS_SIGN) {
                            break;
                        }
					} // end if: quartet built

				} // end if: equals sign or better

			} // end if: white space, equals sign or better
			else {
				System.err.println("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
				return null;
			} // end else:
		} // each input character

		byte[] out = new byte[outBuffPosn];
		System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
		return out;
	} // end decode

	
	
	/**
	 * 内部项目使用的解码方法
	 * @author 刘虻
	 * 2008-7-9上午08:09:54
	 * @param source        源
	 * @param srcOffset     源起始字符位置
	 * @param destination   母鸡
	 * @param destOffset    母鸡
	 * @return 母鸡
	 */
	protected static int nativeDecode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset) {
		// Example: Dk==
		if (source[srcOffset + 2] == EQUALS_SIGN) {
			// Two ways to do the same thing. Don't know which way I like best.
			// int outBuff = ( ( base64Alphabet[ source[ srcOffset ] ] << 24 ) >>> 6
			// )
			// | ( ( base64Alphabet[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
			int outBuff = ((nativeBase64Alphabet[source[srcOffset]] & 0xFF) << 18)
					| ((nativeBase64Alphabet[source[srcOffset + 1]] & 0xFF) << 12);

			destination[destOffset] = (byte) (outBuff >>> 16);
			return 1;
		}
		// Example: DkL=
		else if (source[srcOffset + 3] == EQUALS_SIGN) {
			// Two ways to do the same thing. Don't know which way I like best.
			int outBuff = ((nativeBase64Alphabet[source[srcOffset]] & 0xFF) << 18)
					| ((nativeBase64Alphabet[source[srcOffset + 1]] & 0xFF) << 12)
					| ((nativeBase64Alphabet[source[srcOffset + 2]] & 0xFF) << 6);

			destination[destOffset] = (byte) (outBuff >>> 16);
			destination[destOffset + 1] = (byte) (outBuff >>> 8);
			return 2;
		}
		// Example: DkLE
		else {
			try {
				// Two ways to do the same thing. Don't know which way I like
				// best.
				int outBuff = ((nativeBase64Alphabet[source[srcOffset]] & 0xFF) << 18)
						| ((nativeBase64Alphabet[source[srcOffset + 1]] & 0xFF) << 12)
						| ((nativeBase64Alphabet[source[srcOffset + 2]] & 0xFF) << 6)
						| ((nativeBase64Alphabet[source[srcOffset + 3]] & 0xFF));

				destination[destOffset] = (byte) (outBuff >> 16);
				destination[destOffset + 1] = (byte) (outBuff >> 8);
				destination[destOffset + 2] = (byte) (outBuff);

				return 3;
			} catch (Exception e) {
				System.out.println("" + source[srcOffset] + ": " + (nativeBase64Alphabet[source[srcOffset]]));
				System.out.println("" + source[srcOffset + 1] + ": " + (nativeBase64Alphabet[source[srcOffset + 1]]));
				System.out.println("" + source[srcOffset + 2] + ": " + (nativeBase64Alphabet[source[srcOffset + 2]]));
				System.out.println("" + source[srcOffset + 3] + ": " + (nativeBase64Alphabet[source[srcOffset + 3]]));
				return -1;
			} // e nd catch
		}
	} // end decodeToBytes

	
	/**
	 * 母鸡
	 * @author 刘虻
	 * 2008-7-9上午08:09:54
	 * @param source      母鸡
	 * @param srcOffset   母鸡
	 * @param destination 母鸡
	 * @param destOffset  母鸡
	 * @return 母鸡
	 */
	protected static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset) {
		// Example: Dk==
		if (source[srcOffset + 2] == EQUALS_SIGN) {
			// Two ways to do the same thing. Don't know which way I like best.
			// int outBuff = ( ( base64Alphabet[ source[ srcOffset ] ] << 24 ) >>> 6
			// )
			// | ( ( base64Alphabet[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
			int outBuff = ((base64Alphabet[source[srcOffset]] & 0xFF) << 18)
					| ((base64Alphabet[source[srcOffset + 1]] & 0xFF) << 12);

			destination[destOffset] = (byte) (outBuff >>> 16);
			return 1;
		}
		// Example: DkL=
		else if (source[srcOffset + 3] == EQUALS_SIGN) {
			// Two ways to do the same thing. Don't know which way I like best.
			// int outBuff = ( ( base64Alphabet[ source[ srcOffset ] ] << 24 ) >>> 6
			// )
			// | ( ( base64Alphabet[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
			// | ( ( base64Alphabet[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
			int outBuff = ((base64Alphabet[source[srcOffset]] & 0xFF) << 18)
					| ((base64Alphabet[source[srcOffset + 1]] & 0xFF) << 12)
					| ((base64Alphabet[source[srcOffset + 2]] & 0xFF) << 6);

			destination[destOffset] = (byte) (outBuff >>> 16);
			destination[destOffset + 1] = (byte) (outBuff >>> 8);
			return 2;
		}
		// Example: DkLE
		else {
			try {
				// Two ways to do the same thing. Don't know which way I like
				// best.
				// int outBuff = ( ( base64Alphabet[ source[ srcOffset ] ] << 24 )
				// >>> 6 )
				// | ( ( base64Alphabet[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
				// | ( ( base64Alphabet[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
				// | ( ( base64Alphabet[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
				int outBuff = ((base64Alphabet[source[srcOffset]] & 0xFF) << 18)
						| ((base64Alphabet[source[srcOffset + 1]] & 0xFF) << 12)
						| ((base64Alphabet[source[srcOffset + 2]] & 0xFF) << 6)
						| ((base64Alphabet[source[srcOffset + 3]] & 0xFF));

				destination[destOffset] = (byte) (outBuff >> 16);
				destination[destOffset + 1] = (byte) (outBuff >> 8);
				destination[destOffset + 2] = (byte) (outBuff);
				return 3;
			} catch (Exception e) {
				System.out.println("" + source[srcOffset] + ": " + (base64Alphabet[source[srcOffset]]));
				System.out.println("" + source[srcOffset + 1] + ": " + (base64Alphabet[source[srcOffset + 1]]));
				System.out.println("" + source[srcOffset + 2] + ": " + (base64Alphabet[source[srcOffset + 2]]));
				System.out.println("" + source[srcOffset + 3] + ": " + (base64Alphabet[source[srcOffset + 3]]));
				return -1;
			} // e nd catch
		}
	} // end decodeToBytes

	

	/**
	 * 解码
	 * @author 刘虻
	 * 2008-7-9上午08:09:40
	 * @param _s   源
	 * @param _enc 解码后的字符串编码
	 * @return     解码后的信息
	 */
	public final static String base64Decode(String _s, String _enc) {
		if (_s == null) {
            return null;
        }
		if (_enc == null) {
			_enc = "ISO_8859_1";
		}
		//如果存在被Base64加密的标识头，则去掉标识头
		if(_s.startsWith("@b64@")) {
			_s = _s.substring(5);
		}
		try {
			return new String(decode64(_s), _enc);
		} catch (UnsupportedEncodingException uee) {
		}
		return null;
	}

	
	/**
	 * 项目内部使用的Base64解码方法
	 * @param str      待处理字符串
	 * @param enc      处理后的字符串编码
	 * @return         处理后的字符串
	 * 2018年4月25日
	 * @author MBG
	 */
	public final static String nativeD64(String str,String enc) {
		if (str == null) {
            return null;
        }
		if (enc == null || enc.length()<1) {
			enc = "ISO_8859_1";
		}
		//如果存在被Base64加密的标识头，则去掉标识头
		if(str.startsWith("@b64@")) {
			str = str.substring(5);
		}
		try {
			return new String(nativeD64(str), enc);
		} catch (UnsupportedEncodingException uee) {
		}
		return null;
	}
	
	
	/**
	 * Base64编码，编码后再通过URL格式编码（通常用于POST提交Base64值）
	 * @param _s     待编码的字符串
	 * @param _enc   字符串编码格式（默认UTF-8 )
	 * @return
	 * 2020年4月3日
	 * @author MBG
	 */
	public final static String urlE64(String _s,String _enc) {
		if(_enc==null || _enc.length()<1) {
			_enc = "UTF-8";
		}
        try {
            return URLEncoder.encode(base64Encode(_s,_enc),_enc);
        } catch (Exception e) {
            return _s;
        }
	}
	

	/**
	 * Base64编码
	 * @author 刘虻
	 * 2008-7-9上午07:48:42
	 * @param _s      待处理字符串
	 * @param _enc    处理后的编码
	 * @return        处理后的字符串
	 */
	public final static String base64Encode(String _s, String _enc) {
		if (_s == null) {
            return null;
        }
		if (_enc == null) {
            _enc = "ISO_8859_1";
        }
		try {
			return base64Encode(_s.getBytes(_enc));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	
	/**
	 * 内部项目使用的编码方法
	 * @author 刘虻
	 * 2008-7-9上午07:48:42
	 * @param _s     待处理字符串
	 * @param _enc   处理后的编码
	 * @return       处理后的字符串
	 */
	public final static String nativeE64(String _s, String _enc) {
		if (_s == null) {
            return null;
        }
		if (_enc == null) {
            _enc = "ISO_8859_1";
        }
		try {
			return nativeE64(_s.getBytes(_enc));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 进行Base64解码
	 * @author 刘虻
	 * 2008-7-9上午08:10:19
	 * @param s 母鸡
	 * @return 母鸡
	 */
	public static byte[] decode64(String s) {
		byte[] bytes;
		try {
			bytes = s.getBytes("ISO_8859_1");
		} // end try
		catch (java.io.UnsupportedEncodingException uee) {
			bytes = s.getBytes();
		} // end catch
		// </change>

		// Decode
		bytes = decode(bytes, 0, bytes.length);

		// Check to see if it's gzip-compressed
		// GZIP Magic Two-Byte Number: 0x8b1f (35615)
		if (bytes != null && bytes.length >= 4) {

			int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
			if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
				java.io.ByteArrayInputStream bais = null;
				java.util.zip.GZIPInputStream gzis = null;
				java.io.ByteArrayOutputStream baos = null;
				byte[] buffer = new byte[2048];
				int length = 0;

				try {
					baos = new java.io.ByteArrayOutputStream();
					bais = new java.io.ByteArrayInputStream(bytes);
					gzis = new java.util.zip.GZIPInputStream(bais);

					while ((length = gzis.read(buffer)) >= 0) {
						baos.write(buffer, 0, length);
					} // end while: reading input

					// No error? Get new bytes.
					bytes = baos.toByteArray();

				} // end try
				catch (java.io.IOException e) {
					// Just return originally-decoded bytes
				} // end catch
				finally {
					try {
						baos.close();
					} catch (Exception e) {
					}
					try {
						gzis.close();
					} catch (Exception e) {
					}
					try {
						bais.close();
					} catch (Exception e) {
					}
				} // end finally

			} // end if: gzipped
		} // end if: bytes.length >= 2

		return bytes;
	} // end decode
	
	
	
	/**
	 * 项目内部使用的Base64解码
	 * @author 刘虻
	 * 2008-7-9上午08:10:19
	 * @param s   待处理字符串
	 * @return    处理后的字符串
	 */
	private static byte[] nativeD64(String s) {
		byte[] bytes;
		try {
			bytes = s.getBytes("ISO_8859_1");
		} // end try
		catch (java.io.UnsupportedEncodingException uee) {
			bytes = s.getBytes();
		} // end catch
		// </change>

		// Decode
		bytes = nativeDecode(bytes, 0, bytes.length);

		// Check to see if it's gzip-compressed
		// GZIP Magic Two-Byte Number: 0x8b1f (35615)
		if (bytes != null && bytes.length >= 4) {

			int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
			if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
				java.io.ByteArrayInputStream bais = null;
				java.util.zip.GZIPInputStream gzis = null;
				java.io.ByteArrayOutputStream baos = null;
				byte[] buffer = new byte[2048];
				int length = 0;

				try {
					baos = new java.io.ByteArrayOutputStream();
					bais = new java.io.ByteArrayInputStream(bytes);
					gzis = new java.util.zip.GZIPInputStream(bais);

					while ((length = gzis.read(buffer)) >= 0) {
						baos.write(buffer, 0, length);
					} // end while: reading input

					// No error? Get new bytes.
					bytes = baos.toByteArray();

				} // end try
				catch (java.io.IOException e) {
					// Just return originally-decoded bytes
				} // end catch
				finally {
					try {
						baos.close();
					} catch (Exception e) {
					}
					try {
						gzis.close();
					} catch (Exception e) {
					}
					try {
						bais.close();
					} catch (Exception e) {
					}
				} // end finally

			} // end if: gzipped
		} // end if: bytes.length >= 2

		return bytes;
	} // end decode
	
	/**
	 * 按照RFC2405标准，输出的Base64编码，在第76个字符时换行
	 * @param _bytes 需要编码的字节数组
	 * @return       编码后的字符串
	 * 2018年11月29日
	 * @author MBG
	 */
	public final static String rfc2405Encode(byte[] _bytes) {
		String sparator = System.getProperty("line.separator");
		String res = base64Encode(_bytes);
		StringBuffer reSbf = new StringBuffer();
		boolean noFirst = false;
		while(res.length()>76) {
			if(noFirst) {
				reSbf.append(sparator);
			}else {
				noFirst = true;
			}
			reSbf.append(res, 0, 76);
			res = res.substring(76);
		}
		if(res.length()>0) {
			if(noFirst) {
				reSbf.append(sparator);
			}
			reSbf.append(res);
		}
		return reSbf.toString();
	}
	
	
	/**
	 * Base64编码
	 * @author 刘虻
	 * 2008-7-9上午08:07:37
	 * @param _bytes 待处理信息
	 * @return       处理后的字符串
	 */
	public final static String base64Encode(byte[] _bytes) {
		StringBuffer encodedBuffer = new StringBuffer((int) (_bytes.length * 1.5));
		int i = 0;
		int pad = 0;
		while (i < _bytes.length) {
			int b1 = (0xFF & _bytes[i++]);
			int b2;
			int b3;
			if (i >= _bytes.length) {
				b2 = 0;
				b3 = 0;
				pad = 2;
			} else {
				b2 = 0xFF & _bytes[i++];
				if (i >= _bytes.length) {
					b3 = 0;
					pad = 1;
				} else {
                    b3 = (0xFF & _bytes[i++]);
                }
			}
			byte c1 = (byte) (b1 >> 2);
			byte c2 = (byte) (((b1 & 0x3) << 4) | (b2 >> 4));
			byte c3 = (byte) (((b2 & 0xf) << 2) | (b3 >> 6));
			byte c4 = (byte) (b3 & 0x3f);
			encodedBuffer.append((char)lookUpBase64Alphabet[c1]).append((char)lookUpBase64Alphabet[c2]);
			switch (pad) {
			case 0:
				encodedBuffer.append((char)lookUpBase64Alphabet[c3]).append((char)lookUpBase64Alphabet[c4]);
				break;
			case 1:
				encodedBuffer.append((char)lookUpBase64Alphabet[c3]).append('=');
				break;
			case 2:
				encodedBuffer.append("==");
				break;
			}
		}
		return encodedBuffer.toString();
	}
	
	
	/**
	 * 内部项目使用的Base64编码
	 * @author 刘虻
	 * 2008-7-9上午08:07:37
	 * @param _bytes 待处理信息
	 * @return       处理后的字符串
	 */
	public final static String nativeE64(byte[] _bytes) {
		StringBuffer encodedBuffer = new StringBuffer((int) (_bytes.length * 1.5));
		int i = 0;
		int pad = 0;
		while (i < _bytes.length) {
			int b1 = (0xFF & _bytes[i++]);
			int b2;
			int b3;
			if (i >= _bytes.length) {
				b2 = 0;
				b3 = 0;
				pad = 2;
			} else {
				b2 = 0xFF & _bytes[i++];
				if (i >= _bytes.length) {
					b3 = 0;
					pad = 1;
				} else {
                    b3 = (0xFF & _bytes[i++]);
                }
			}
			byte c1 = (byte) (b1 >> 2);
			byte c2 = (byte) (((b1 & 0x3) << 4) | (b2 >> 4));
			byte c3 = (byte) (((b2 & 0xf) << 2) | (b3 >> 6));
			byte c4 = (byte) (b3 & 0x3f);
			encodedBuffer.append((char)nativeLookUpBase64Alphabet[c1]).append((char)nativeLookUpBase64Alphabet[c2]);
			switch (pad) {
			case 0:
				encodedBuffer.append((char)nativeLookUpBase64Alphabet[c3]).append((char)nativeLookUpBase64Alphabet[c4]);
				break;
			case 1:
				encodedBuffer.append((char)nativeLookUpBase64Alphabet[c3]).append('=');
				break;
			case 2:
				encodedBuffer.append("==");
				break;
			}
		}
		return encodedBuffer.toString();
	}
	
	/**
	 * Encodes hex octects into Base64.
	 *
	 * @param binaryData Array containing binary data to encode.
	 * @return Base64-encoded data.
	 */
	public static byte[] encode( byte[] binaryData){
		int	  lengthDataBits	= binaryData.length*EIGHTBIT;
		int	  fewerThan24bits   = lengthDataBits%TWENTYFOURBITGROUP;
		int	  numberTriplets	= lengthDataBits/TWENTYFOURBITGROUP;
		byte[] encodedData = null;
		if (fewerThan24bits != 0){
			//data not divisible by 24 bit
			encodedData = new byte[ (numberTriplets + 1 ) * 4 ];
		}else{
			// 16 or 8 bit
			encodedData = new byte[ numberTriplets * 4 ];
		}
		byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
		int encodedIndex = 0;
		int dataIndex   = 0;
		int i		   = 0;
		//log.debug("number of triplets = " + numberTriplets);
		for ( i = 0; i<numberTriplets; i++){
			dataIndex = i*3;
			b1 = binaryData[dataIndex];
			b2 = binaryData[dataIndex + 1];
			b3 = binaryData[dataIndex + 2];

			//log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);

			l  = (byte)(b2 & 0x0f);
			k  = (byte)(b1 & 0x03);

			encodedIndex = i * 4;
			byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
			byte val2 = ((b2 & SIGN)==0)?(byte)(b2>>4):(byte)((b2)>>4^0xf0);
			byte val3 = ((b3 & SIGN)==0)?(byte)(b3>>6):(byte)((b3)>>6^0xfc);

			encodedData[encodedIndex]   = lookUpBase64Alphabet[ val1 ];
			//log.debug( "val2 = " + val2 );
			//log.debug( "k4   = " + (k<<4) );
			//log.debug(  "vak  = " + (val2 | (k<<4)) );
			encodedData[encodedIndex+1] =
				lookUpBase64Alphabet[ val2 | ( k<<4 )];
			encodedData[encodedIndex+2] =
				lookUpBase64Alphabet[ (l <<2 ) | val3 ];
			encodedData[encodedIndex+3] = lookUpBase64Alphabet[ b3 & 0x3f ];
		}
		// form integral number of 6-bit groups
		dataIndex	= i*3;
		encodedIndex = i*4;
		if (fewerThan24bits == EIGHTBIT ){
			b1 = binaryData[dataIndex];
			k = (byte) ( b1 &0x03 );
			//log.debug("b1=" + b1);
			//log.debug("b1<<2 = " + (b1>>2) );
			byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
			encodedData[encodedIndex]	 = lookUpBase64Alphabet[ val1 ];
			encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ k<<4 ];
			encodedData[encodedIndex + 2] = PAD;
			encodedData[encodedIndex + 3] = PAD;
		}else if (fewerThan24bits == SIXTEENBIT){

			b1 = binaryData[dataIndex];
			b2 = binaryData[dataIndex +1 ];
			l = (byte) (b2 & 0x0f);
			k = (byte) (b1 & 0x03);

			byte val1 = ((b1 & SIGN) == 0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
			byte val2 = ((b2 & SIGN) == 0)?(byte)(b2>>4):(byte)((b2)>>4^0xf0);

			encodedData[encodedIndex]	 = lookUpBase64Alphabet[ val1 ];
			encodedData[encodedIndex + 1] =
				lookUpBase64Alphabet[ val2 | ( k<<4 )];
			encodedData[encodedIndex + 2] = lookUpBase64Alphabet[ l<<2 ];
			encodedData[encodedIndex + 3] = PAD;
		}

		return encodedData;
	}

	/**
	 * Decodes Base64 data into octects
	 *
	 * @param base64Data Byte array containing Base64 data
	 * @return Array containing decoded data.
	 */
	public static byte[] decode( byte[] base64Data ){
		// handle the edge case, so we don't have to worry about it later
		if(base64Data.length == 0) { 
			return new byte[0]; 
		}

		int	  numberQuadruple	= base64Data.length/FOURBYTE;
		byte[] decodedData = null;
		byte	 b1=0,b2=0,b3=0, b4=0, marker0=0, marker1=0;

		// Throw away anything not in base64Data

		int encodedIndex = 0;
		int dataIndex	= 0;
		{
			// this sizes the output array properly - rlw
			int lastData = base64Data.length;
			// ignore the '=' padding
			while (base64Data[lastData-1] == PAD)
			{
				if (--lastData == 0)
				{
					return new byte[0];
				}
			}
			decodedData = new byte[ lastData - numberQuadruple ];
		}

		for (int i = 0; i < numberQuadruple; i++){
			dataIndex = i * 4;
			marker0   = base64Data[dataIndex + 2];
			marker1   = base64Data[dataIndex + 3];

			b1 = base64Alphabet[base64Data[dataIndex]];
			b2 = base64Alphabet[base64Data[dataIndex +1]];

			if (marker0 != PAD && marker1 != PAD){
				//No PAD e.g 3cQl
				b3 = base64Alphabet[ marker0 ];
				b4 = base64Alphabet[ marker1 ];

				decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 ) ;
				decodedData[encodedIndex + 1] =
					(byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) );
				decodedData[encodedIndex + 2] = (byte)( b3<<6 | b4 );
			}else if (marker0 == PAD){
				//Two PAD e.g. 3c[Pad][Pad]
				decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 ) ;
			}else if (marker1 == PAD){
				//One PAD e.g. 3cQ[Pad]
				b3 = base64Alphabet[ marker0 ];

				decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 );
				decodedData[encodedIndex + 1] =
					(byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) );
			}
			encodedIndex += 3;
		}
		return decodedData;
	}
}
